﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Specialized;
using Xceed.Silverlight.Utils;
using System.Collections.Generic;

namespace Xceed.Silverlight.Controls.Utils
{
  internal class NotifyCollectionChangedManager
  {
    private NotifyCollectionChangedManager()
    {
    }

    #region Singleton Static Property

    private static NotifyCollectionChangedManager Singleton
    {
      get
      {
        if( ms_singleton == null )
          ms_singleton = new NotifyCollectionChangedManager();

        return ms_singleton;
      }
    }

    private static NotifyCollectionChangedManager ms_singleton = null;

    #endregion Singleton Static Property

    public static void AddListener( INotifyCollectionChanged source,
        IWeakEventListener listener )
    {
      NotifyCollectionChangedManager.Singleton.PrivateAddListener( source, listener );
    }

    public static void RemoveListener( INotifyCollectionChanged source,
        IWeakEventListener listener )
    {
      NotifyCollectionChangedManager.Singleton.PrivateRemoveListener( source, listener );
    }

    private void OnCollectionChanged( object sender, EventArgs args )
    {
      List<WeakReference> listeners;

      if( !m_dictionary.TryGetValue( ( INotifyCollectionChanged )sender, out listeners ) )
        return;

      foreach( WeakReference listener in listeners )
      {
        IWeakEventListener eventItem = listener.Target as IWeakEventListener;
        if( eventItem != null && listener.IsAlive )
        {
          eventItem.ReceiveWeakEvent( this.GetType(), sender, args );
        }
      }
    }

    private void PrivateAddListener( INotifyCollectionChanged source, IWeakEventListener listener )
    {
      lock( m_syncLock )
      {
        WeakReference weakRef = new WeakReference( listener );

        bool newSource = false;

        List<WeakReference> listeners;
        if( !m_dictionary.TryGetValue( source, out listeners ) )
        {
          listeners = new List<WeakReference>();
          m_dictionary.Add( source, listeners );
          listeners.Add( weakRef );
          newSource = true;
        }
        else
        {
          bool needAdd = true;
          foreach( WeakReference existingWeakRefs in listeners )
          {
            if( ( existingWeakRefs.Target != null ) && existingWeakRefs.Target.Equals( listeners ) )
            {
              needAdd = false;
              break;
            }
          }

          if( needAdd )
          {
            listeners.Add( weakRef );
          }
        }

        if( newSource )
        {
          source.CollectionChanged += this.OnCollectionChanged;
        }
      }
    }

    private void PrivateRemoveListener( INotifyCollectionChanged source, IWeakEventListener listener )
    {
      lock( m_syncLock )
      {
        bool unregister = false;

        List<WeakReference> listeners;

        if( m_dictionary.TryGetValue( source, out listeners ) )
        {
          WeakReference removed = null;
          foreach( WeakReference weakRef in listeners )
          {
            if( ( weakRef.Target != null ) && ( weakRef.Target.Equals( listener ) ) )
            {
              removed = weakRef;
              break;
            }
          }

          if( removed != null )
          {
            listeners.Remove( removed );
          }

          if( listeners.Count == 0 )
          {
            m_dictionary.Remove( source );
            unregister = true;
          }
        }

        if( unregister )
        {
          source.CollectionChanged -= this.OnCollectionChanged;
        }
      }
    }

    private Dictionary<INotifyCollectionChanged, List<WeakReference>> m_dictionary = new Dictionary<INotifyCollectionChanged, List<WeakReference>>();
    private static object m_syncLock = new object();
  }
}
